#ifndef _game_Team_hpp_
#define _game_Team_hpp_

/** \file Team.hpp
 *  \author Adrian Schweizer
 *  \created  $Sa 18 Aug 03:48:01 pm CEST 2007 schwadri@SchwadriComp.local$
 *  \modified $So 30 Sep 06:21:21 pm CEST 2007 schwadri@SchwadriLaptop$
 */

#include <vector>
#include <algorithm>
#include <string>

#include <boost/serialization/access.hpp>

#include "../network/Types.hpp"
#include "Common.hpp"
#include "Player.hpp"

namespace game {

    //Forward decl
    class TeamManager;

    /** \brief A Container for multiple Player objects
     *
     *  \todo
     *  - Signal behaviour not implemented
     */
    class Team
    {
    public:
        static const int LIMIT_TEAM_SIZE;   ///< maximum number of players per team

        ~Team();
    private:
        typedef std::vector<Player>                 player_container;
    public:

        typedef player_container::iterator          iterator;
        typedef player_container::const_iterator    const_iterator;
        typedef player_container::iterator          player_iterator;
        typedef player_container::const_iterator    const_player_iterator;
		typedef player_container::size_type			size_type;

        //FIXME: noncopyable is your friend or maybe not. well here it isn't :-(
        /** \name Signal Types */ //@{
        //typedef boost::signal<void()> ChangedSignalType; ///< type of ChangedSignal
        //@}

        /** \name Slot Types */ //@{
        //typedef ChangedSignalType::slot_type ChangedSlotType; ///< type of functor(s) invoked on a ChangedSignalType
        //@}

        /** \name Signals*///@{
        /** \brief emitted whenever a Player has been added to the team or removed
         *
         *  \note Only use this to notify UI components or general views that the underlying model has changed.
         */
        //mutable ChangedSignalType           ChangedSignal;

        //@}


        /** \name Iterator methods *///@{
        player_iterator player_begin()              { return m_players.begin(); }
        player_iterator player_end()                { return m_players.end(); }

        iterator        begin()         { return m_players.begin(); }
        iterator        end()           { return m_players.end(); }

        const_player_iterator player_begin() const  { return m_players.begin(); }
        const_player_iterator player_end()   const  { return m_players.end(); }
        //@}

        /** \name Player Container Functionality*///@{
        Player&         operator[](char pid)        { return m_players[pid]; }
        const Player&   operator[](char pid) const  { return m_players[pid]; }

        /** \brief Clears the internal Player Container
         *
         *  \par Signals:
         *  ChangedSignal
         */
        void            Clear();

        /** \brief Push Back a new Player
         *
         *  \par Signals:
         *  ChangedSignal
         */
        void            PushBack(const Player& p);

        /** \brief Push Back a new Player
         *
         *  \par Signals:
         *  ChangedSignal
         */
        void            PushBack(network::Client::Id cid);

        /** \brief Remove an existing Player
         *
         *  \par Signals:
         *  ChangedSignal
         */
        void            Remove(Player::Id pid);

        /** \brief Remove an existing Player
         *
         *  \par Signals:
         *  ChangedSignal
         */
        void            Remove(const Player& p);

        /** \brief Remove an existing Player by its ClientId
         *
         *  \par Signals:
         *  ChangedSignal
         */
        void            Remove(network::Client::Id cid);

        /** \brief Remove an existing Player by its ClientId
         *
         *  \param[in] piter    A valid player_iterator into the internal container. If not
         *  valid behaviour is undefined!
         *  \return a player_iterator pointing to the entry after the supplied iterator
         *  \par Signals:
         *  ChangedSignal
         */
        player_iterator Remove(player_iterator piter);

        /** \brief Remove an existing Player by its internal idx
         *
         *  \par Signals:
         *  ChangedSignal
         */
        void            Remove(char pidx);
        //@}
        /** \brief accepts a modifying Visitor*/
        template<class Visitor>
            void        Accept(Visitor & v)
            {
                foreach(Player & p, m_players)
                    v.Visit(p);
            }
        /** \name Accessors*///@{
        const std::string&  Name() const                { return m_name; }          ///< get the name of this team
        char                OpenPlayerSlots() const     { return m_max_players; }   ///< get the number of open player slots
        int					Count() const               { return static_cast<int>(m_players.size()); }///< get the number of occupied player slots
        /** \brief get the number of unoccupied, open slots*/
        int					AvailablePlayerSlots() const{ return std::max(m_max_players- static_cast<int>(m_players.size()),0); }
        bool                Empty() const               { return m_players.empty(); }///< returns true if this Team has no Player
        /** \brief gets the TeamId. This can be used to get a reference to this Team from the TeamManager*/
        char                TeamId() const              { return m_tid; }
        //@}
        /** \name Mutators*///@{

        /** \brief Change the number of Open Player slots
         *
         *  \note Players occupying now "closed" slots will not be removed from the team
         *  \par Signals:
         *  ChangedSignal
         */
        void                OpenPlayerSlots(int maxp)      { m_max_players = std::max(std::min(maxp,LIMIT_TEAM_SIZE),0); }

        /** \brief Change the Team Name
         *
         *  \par Signals:
         *  ChangedSignal
         */
        void                Name(const std::string& name)  { m_name = name; }
        //@}

        friend class boost::serialization::access;
     private:
        /** \brief serialize method for boost::archive compatible archives*/
        template<class Archive>
            void serialize(Archive & ar, const unsigned int version)
            {
                ar & m_tid;
                ar & m_max_players;
                ar & m_players;
                ar & m_name;
            }

        Team(TeamManager* tm, char tid=-1);

        /** \brief sets a new team id and updates the players ids accordingly*/
        void            TeamId(char tid);
        /** \brief fixes all the player ids in the range [piter,m_players.end())*/
        void                ShiftPlayersIndex(player_iterator piter);
        TeamManager*        m_team_manager;         ///< Pointer to an TeamManager who this Team object belongs to
        char                m_tid;
        int                 m_max_players;
        player_container    m_players;
        std::string         m_name;         ///< The teams name
        friend class TeamManager;
    };

} // namespace game

#endif // _game_Team_hpp_
